//
//  Lattice.cpp
//  phi_four
//
//  Created by Snir Gazit on 3/8/12.
//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

#include "Lattice.h"
// Site

//Lattice
void Lattice::initSquare(boost::array<int, DIM> const &latSize) {
	cordinationNumber_ = 2 * DIM;
	siteLattice_.init(latSize);
	XYBondLattice_.init(latSize);
	latSize_ = latSize;
	SiteIterIndex iter = siteLattice_.beginIndex();
	for (; iter != siteLattice_.endIndex(); iter++) {
		IndexType currentPos = iter.getIndex();
		iter->siteIndex_ = currentPos;
		XYBondLattice_(currentPos).resize(DIM);
		iter->totalBonds_.fill(0.);
		for (int d = 0; d < DIM; d++) {
			// XY Bonds
			for (int loopType = 0; loopType < 2; loopType++) {
				XYBondType& currentbondXY = XYBondLattice_(currentPos)[d];
				currentbondXY.bondVal[loopType][LEFT] =
						currentbondXY.bondVal[loopType][RIGHT] = 0;
				currentbondXY.dir = d;
				// Update NN
				boost::array<int, DIM> nPos = currentPos;
				if (nPos[d] == (latSize_[d] - 1)) {
					nPos[d] = 0;
				} else
					nPos[d]++;
				SiteIter nIter = siteLattice_.iterByPos(nPos);
				// XY Bonds
				iter->XYBondArray_[loopType].push_back(
						XYBondData(&currentbondXY, nIter, RIGHT, CREATE));
				iter->XYBondArray_[loopType].push_back(
						XYBondData(&currentbondXY, nIter, LEFT, REMOVE));
				nIter->XYBondArray_[loopType].push_back(
						XYBondData(&currentbondXY, iter.getIter(), LEFT,
								CREATE));
				nIter->XYBondArray_[loopType].push_back(
						XYBondData(&currentbondXY, iter.getIter(), RIGHT,
								REMOVE));
			}
		}
	}
//    for (; iter != siteLattice_.endIndex(); iter++) {
//        std::sort(iter->bondArray_.begin(), iter->bondArray_.end(),
//                  [](BondData bond1,BondData bond2)->bool
//        {
//            if (bond1.dir>bond2.dir) return true;
//            else
//                if (bond.moveDir==LEFT return true
//                    else return false;
//        })
//    }
}

void Lattice::init(boost::array<int, DIM> const &latSize) {
	this->initSquare(latSize);
	ira_ = siteLattice_.begin();
	masha_ = siteLattice_.begin();
//	ira_->totalBonds_ = 1;
//	string outPath = "/home/snirgaz";
//	this->dump(outPath);
//	this->read(outPath);
}

bool Lattice::isClosed() const {
	return ira_ == masha_;
}
Lattice::SiteIter Lattice::getMasha() {
	return masha_;
}
Lattice::SiteIter Lattice::getIra() {
	return ira_;
}
void Lattice::setMasha(Lattice::SiteIter &iter) {
	masha_ = iter;
}
void Lattice::setIra(Lattice::SiteIter &iter) {
	ira_ = iter;
}
int Lattice::getCordinationNumber() const {
	return cordinationNumber_;
}

int Lattice::getNumOfSites() const {
	return siteLattice_.totalSize();
}

Lattice::SiteIter Lattice::getSite(int distanceFromStart) {
	return siteLattice_.begin() + distanceFromStart;
}
boost::array<int, DIM> Lattice::getSizes() {
	return latSize_;
}
int Lattice::getTotalBonds() const{
	return totalBonds_;
}
void Lattice::setToalBonds(int totalBonds){
	totalBonds_=totalBonds;
}
bool Lattice::testConfiguration(int &totalBonds) {
	totalBonds=0;
	SiteIter it = siteLattice_.begin();
	for (; it != siteLattice_.end(); it++) {
		for (int loopType = 0; loopType < 2; loopType++) {
			int count = 0;
			auto itbXY = it->XYBondArray_[loopType].begin();
			for (; itbXY != it->XYBondArray_[loopType].end(); itbXY++) {
				if (itbXY->createRemove_ == REMOVE)
					continue;
				if (itbXY->moveDir_ == RIGHT) {
					count += itbXY->bond_->bondVal[loopType][0];
					assert(itbXY->bond_->bondVal[loopType][0] >= 0);
					count -= itbXY->bond_->bondVal[loopType][1];
					assert(itbXY->bond_->bondVal[loopType][1] >= 0);
				} else {
					count -= itbXY->bond_->bondVal[loopType][0];
					assert(itbXY->bond_->bondVal[loopType][0] >= 0);
					count += itbXY->bond_->bondVal[loopType][1];
					assert(itbXY->bond_->bondVal[loopType][1] >= 0);
				}
				totalBonds+=itbXY->bond_->bondVal[loopType][0]+itbXY->bond_->bondVal[loopType][1];
			}
			if (count != 0) {
				return false;
			}
		}
	}
	return true;
}
Lattice::XYBondArrayIter Lattice::XYBondBegin() {
	return XYBondLattice_.begin();
}
Lattice::XYBondArrayIter Lattice::XYBondEnd() {
	return XYBondLattice_.end();
}
Lattice::SiteIter Lattice::siteBegin() {
	return siteLattice_.begin();
}
Lattice::SiteIter Lattice::siteEnd() {
	return siteLattice_.end();
}

void Lattice::dump(string fileName) {
	try{
	bool testConf=testConfiguration(totalBonds_);
	if (!testConf) throw "Bad Configuration Dumping";
	}
	catch (string &e){
		cout << e;
		exit(1);
	}
	fileName += "/Lat.dat.zip";
	namespace io = boost::iostreams;
	namespace ar = boost::archive;
	ofstream file(fileName.c_str(),
			std::ios::out | std::ios::binary | std::ios::trunc);
	io::filtering_ostream out;
	out.push(io::zlib_compressor());
	out.push(file);
	ar::text_oarchive oa(out);
//	ofstream file(fileName.c_str(), ios_base::out);
//	ar::text_oaromchive oa(file);
	oa << *this;

}
void Lattice::read(string fileName) {
		fileName += "/Lat.dat.zip";
		namespace io = boost::iostreams;
		namespace ar = boost::archive;
		ifstream file(fileName.c_str(), ios_base::in | ios_base::binary);
		io::filtering_istream in;
		in.push(io::zlib_decompressor());
		in.push(file);
		ar::text_iarchive oi(in);
		oi >> *this;
}
